/*
 * File      : kernal_i2c_ap.c
 * This file is i2C sh1106 driver
 * COPYRIGHT (C) 2020, zc
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-4-30      zc           the first version
 */

/**
 * @addtogroup IMX6ULL
 */
/*@{*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "kernal_i2c_oled.h"

static s32 oled_write_byte(struct i2c_client *client, u8 data, u8 cmd);

//OLED显存总共分为8页
//每页8行，一行128个像素点
//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127 (0~7)行	   
//[1]0 1 2 3 ... 127 (8~15)行	
//[2]0 1 2 3 ... 127 (16~23)行	
//[3]0 1 2 3 ... 127 (24~31)行	
//[4]0 1 2 3 ... 127 (32~39)行	
//[5]0 1 2 3 ... 127 (40~47)行	
//[6]0 1 2 3 ... 127 (48~55)行	
//[7]0 1 2 3 ... 127 (56~63)行			   

//数组每个bit存储OLED每个像素点的颜色值(1-亮(白色),0-灭(黑色))
//每个数组元素表示1列8个像素点，一共128列
static unsigned char OLED_buffer[1024] = 
{
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

//全局的指令定义
#define SH1106_NAME            		"oled-sh1106"
#define I2C_AP_CNT					1

//sh1106设备信息
struct oled_dev {
	dev_t devid;				/* 设备号 	 */
	struct cdev cdev;			/* cdev 	*/
	struct class *class;		/* 类 		*/
	struct device *device;		/* 设备 	 */
	struct device_node	*nd; 	/* 设备节点 */
	int major;					/* 主设备号 */
	void *private_data;			/* 私有数据 		*/
	int cs_gpio;				/* 片选所使用的GPIO编号		*/
};
//oled相关的信息
struct oled_info{
	struct oled_dev dev;
};
struct oled_info oled_sh_info;



void oled_display(struct i2c_client *client)
{
	u8 i,n;		    
	for(i=0;i<OLED_PAGE_SIZE;i++)  
	{  
		oled_write_byte(client, YLevel+i,OLED_CMD);    //设置页地址（0~7）
		oled_write_byte(client, XLevelL,OLED_CMD);      //设置显示位置—列低地址
		oled_write_byte(client, XLevelH,OLED_CMD);      //设置显示位置—列高地址   
		for(n=0;n<WIDTH;n++)
		{
			oled_write_byte(client, OLED_buffer[i*WIDTH+n], OLED_DATA); 
		}
	}   //更新显示
}

void oled_clear(struct i2c_client *client, unsigned dat)  
{  
	if(dat)
	{
		memset(OLED_buffer,0xff,sizeof(OLED_buffer));
	}
	else
	{
		memset(OLED_buffer,0,sizeof(OLED_buffer));
	}
	oled_display(client);
}

/**
 *	oled_write_regs
 * 
 * @param client 	用于操作的I2C设备结构体
 * @param reg 		待写入设备寄存器的首地址
 * @param buf 		待写入数据缓冲区首地址
 * @param len 		待写入数据长度
 *
 * @return 写入寄存的操作结果
 */
static s32 oled_write_regs(struct i2c_client *client, u8 reg, u8 *buf, u8 len)
{
	u8 b[256];
	struct i2c_msg msg;
	
	b[0] = reg;					/* 寄存器首地址 */
	memcpy(&b[1], buf, len);    /* 将要写入的数据拷贝到数组b里面 */
		
	msg.addr = client->addr;	/* 设备地址 */
	msg.flags = 0;				/* 标记为写数据 */

	msg.buf = b;				/* 要写入的数据缓冲区 */
	msg.len = len + 1;			/* 要写入的数据长度 */
	printk("addr:%d, %d, %d\r\n", msg.addr, b[0], b[1]);
	return i2c_transfer(client->adapter, &msg, 1);
}

/**
 *	oled_write_byte
 * 
 * @param client 	用于操作的I2C设备结构体
 * @param data 		写入数据的具体值
 * @param cmd 		写入数据的类型
 *
 * @return 写入寄存的操作结果
 */
static s32 oled_write_byte(struct i2c_client *client, u8 data, u8 cmd)
{
	u8 buf, reg;
	buf = data;
	if(cmd == OLED_CMD)
	{
		reg = 0x00; //指令
	}
	else
	{
		reg = 0x40; //数据
	}
	return oled_write_regs(client, reg, &buf, 1);
}
/**
* 打开设备
* 
* @param inode 驱动内的节点信息
* @param filp  要打开的设备文件(文件描述符) 
*
* @return 设备打开处理结果，0表示正常
*/
static int oled_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &oled_sh_info; /* 设置私有数据 */
	return 0;
}

/**
 * ap设备关闭时执行函数
 * 
 * @param inode 驱动内的节点信息
 * @param filp 要打开的设备文件(文件描述符)
 *
 * @return 设备关闭处理结果，0表示正常
 */
static int oled_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/**
 * 向oled设备写入数据
 * 
 * @param filp  要打开的设备文件(文件描述符)
 * @param buf   待写入数据缓冲的首地址
 * @param count 待写入数据的长度
 * @param f_ops 待写入数据的偏移值
 *
 * @return 设备关闭处理结果，0表示正常
 */
static int oled_write(struct file *filp, const char __user *buf, size_t count,  loff_t *f_pos)
{
	return 0;
}

/* sh1106操作函数 */
static const struct file_operations sh1106_ops = {
	.owner = THIS_MODULE,
	.open = oled_open,
	.write = oled_write,
	.release = oled_release,
};

/**
 * oled设备初始化执行的动作
 * 
 * @param client	设备端接口函数
 *
 * @return NULL
 */
static void oled_init(struct i2c_client *client)
{
	oled_write_byte(client, 0xAE,OLED_CMD);    /*display off*/       
	oled_write_byte(client, 0x02,OLED_CMD);    /*set lower column address*/       
	oled_write_byte(client, 0x10,OLED_CMD);    /*set higher column address*/     
	oled_write_byte(client, 0x40,OLED_CMD);    /*set display start line*/     
	oled_write_byte(client, 0xB0,OLED_CMD);    /*set page address*/     
	oled_write_byte(client, 0x81,OLED_CMD);    /*contract control*/
	oled_write_byte(client, 0xFF,OLED_CMD);    /*128*/     
	oled_write_byte(client, 0xA1,OLED_CMD);    /*set segment remap*/    
	oled_write_byte(client, 0xA6,OLED_CMD);    /*normal / reverse*/     
	oled_write_byte(client, 0xA8,OLED_CMD);    /*multiplex ratio*/
	oled_write_byte(client, 0x3F,OLED_CMD);    /*duty = 1/64*/     
	oled_write_byte(client, 0xAD,OLED_CMD);    /*set charge pump enable*/
	oled_write_byte(client, 0x8B,OLED_CMD);     /*    0x8B    内供VCC   */     
	oled_write_byte(client, 0x32,OLED_CMD);    /*0X30---0X33  set VPP   8V */     
	oled_write_byte(client, 0xC8,OLED_CMD);    /*Com scan direction*/     
	oled_write_byte(client, 0xD3,OLED_CMD);    /*set display offset*/
	oled_write_byte(client, 0x00,OLED_CMD);   /*   0x20  */     
	oled_write_byte(client, 0xD5,OLED_CMD);    /*set osc division*/
	oled_write_byte(client, 0x80,OLED_CMD);         
	oled_write_byte(client, 0xD9,OLED_CMD);    /*set pre-charge period*/
	oled_write_byte(client, 0x1F,OLED_CMD);    /*0x22*/     
	oled_write_byte(client, 0xDA,OLED_CMD);    /*set COM pins*/
	oled_write_byte(client, 0x12,OLED_CMD);     
	oled_write_byte(client, 0xDB,OLED_CMD);    /*set vcomh*/
	oled_write_byte(client, 0x40,OLED_CMD);                
	oled_write_byte(client, 0xAF,OLED_CMD);    /*display ON*/    
}

/**
 * i2c驱动的probe函数，执行设备的模块加载
 * 
 * @param client	设备端接口函数
 * @param id	  	设备端id信息
 *
 * @return 设备probe处理结果
 */
static int sh1106_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	/* 1、构建设备号 */
	if (oled_sh_info.dev.major) {
		oled_sh_info.dev.devid = MKDEV(oled_sh_info.dev.major, 0);
		register_chrdev_region(oled_sh_info.dev.devid, I2C_AP_CNT, SH1106_NAME);
	} else {
		alloc_chrdev_region(&oled_sh_info.dev.devid, 0, I2C_AP_CNT, SH1106_NAME);
		oled_sh_info.dev.major = MAJOR(oled_sh_info.dev.devid);
	}

	/* 2、注册设备 */
	cdev_init(&oled_sh_info.dev.cdev, &sh1106_ops);
	cdev_add(&oled_sh_info.dev.cdev, oled_sh_info.dev.devid, I2C_AP_CNT);

	/* 3、创建类 */
	oled_sh_info.dev.class = class_create(THIS_MODULE, SH1106_NAME);
	if (IS_ERR(oled_sh_info.dev.class)) {
		return PTR_ERR(oled_sh_info.dev.class);
	}

	/* 4、创建设备 */
	oled_sh_info.dev.device = device_create(oled_sh_info.dev.class, NULL, oled_sh_info.dev.devid, NULL, SH1106_NAME);
	if (IS_ERR(oled_sh_info.dev.device)) {
		return PTR_ERR(oled_sh_info.dev.device);
	}

	//获取client的信息
	oled_sh_info.dev.private_data = client;

	oled_init(client);
	oled_clear(client, 1);
	printk(KERN_INFO"Driver %s Init Ok!\r\n", SH1106_NAME);
	return 0;
}

/**
 * iic驱动移除函数
 * 
 * @param spi spi设备   
 *
 * @return 设备移除处理结果
 */
static int sh1106_remove(struct i2c_client *client)
{
	/* 删除设备 */
	cdev_del(&oled_sh_info.dev.cdev);
	unregister_chrdev_region(oled_sh_info.dev.devid, I2C_AP_CNT);

	/* 注销掉类和设备 */
	device_destroy(oled_sh_info.dev.class, oled_sh_info.dev.devid);
	class_destroy(oled_sh_info.dev.class);
	return 0;
}

/* 传统匹配方式ID列表 -- name不重要，但必须存在 */
static const struct i2c_device_id sh1106_id[] = {
	{"oled,sh1106", 0},  
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id sh1106_of_match[] = {
	{ .compatible = "oled,sh1106"},
	{ /* Sentinel */ }
};

/* I2C驱动结构体 */	
static struct i2c_driver sh1106_driver = {
	.probe = sh1106_probe,
	.remove = sh1106_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = SH1106_NAME,
		   	.of_match_table = sh1106_of_match, 
		   },
	.id_table = sh1106_id,
};

/**
 * 驱动加载时执行的初始化函数
 * 
 * @param NULL    
 *
 * @return 驱动加载执行结果
 */
static int __init oled_module_init(void)
{
    return i2c_add_driver(&sh1106_driver);
}

/**
 * 驱动释放时执行的退出函数
 * 
 * @param NULL    
 *
 * @return 驱动退出执行结果
 */
static void __exit oled_module_exit(void)
{
    return i2c_del_driver(&sh1106_driver);
}

module_init(oled_module_init);                      
module_exit(oled_module_exit);
MODULE_AUTHOR("zc");				                //模块作者
MODULE_LICENSE("GPL v2");                           //模块许可协议
MODULE_DESCRIPTION("sh1106 driver");                //模块许描述
MODULE_ALIAS("i2c_sh1106_driver");                  //模块别名

